﻿using System.Reflection.Emit;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Formula;

namespace TrackSystem.IO.Utils
{
    public class ClacDef
    {
        /// <summary>
        /// 距离计算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef Distance_Func(OperandDef[] ops)
        {
            if (ops.Length < 4)
                throw new ArgumentException("dist: 操作数小于4个");

            for(int i = 0; i < 4; i++)
            {
                switch (ops[i].DataType)
                {
                    case DataTypeEnum.BOOLEAN:
                    case DataTypeEnum.STRING:
                        throw new ArgumentException("dist: 操作类型只能是整数或者浮点数");
                    case DataTypeEnum.INTEGER:
                        ops[i].DataType = DataTypeEnum.DOUBLE;
                        ops[i].dVal = ops[1].iVal;
                        break;
                }
            }

            double result = LonLatTools.GetDirectDistance(ops[3].dVal, ops[2].dVal, ops[1].dVal, ops[0].dVal);
            return new OperandDef(result);
        }

        /// <summary>
        /// 方位角计算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef Azimuth_Func(OperandDef[] ops)
        {
            if (ops.Length < 4)
                throw new ArgumentException("azim:　操作数小于4个");

            for (int i = 0; i < 4; i++)
            {
                switch (ops[i].DataType)
                {
                    case DataTypeEnum.BOOLEAN:
                    case DataTypeEnum.STRING:
                        throw new ArgumentException("azim: 操作类型只能是整数或者浮点数");
                    case DataTypeEnum.INTEGER:
                        ops[i].DataType = DataTypeEnum.DOUBLE;
                        ops[i].dVal = ops[1].iVal;
                        break;
                }
            }

            double result = LonLatTools.GetAzimuth(ops[3].dVal, ops[2].dVal, ops[1].dVal, ops[0].dVal);
            return new OperandDef(result);
        }
        
        /// <summary>
        /// 冒号运算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">参数错误</exception>
        public static OperandDef Colon_Func(OperandDef[] ops)
        {
            if (ops.Length < 3)
                throw new ArgumentException("colon: 操作数小于3个");

            if (ops[2].DataType != DataTypeEnum.BOOLEAN)
                throw new ArgumentException("colon: 第一个操作数必须是布尔型");

            return ops[2].bVal ? ops[1] : ops[0];
        }

        /// <summary>
        /// 问号运算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">参数异常</exception>
        public static OperandDef Question_Func(OperandDef[] ops)
        {
            if (ops.Length < 1)
                throw new ArgumentException("question: 操作数小于1个");

            if (ops[0].DataType != DataTypeEnum.BOOLEAN)
                throw new ArgumentException("question: 操作类型必须是布尔型");

            return ops[0];
        }

        /// <summary>
        /// 一元数学运算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <param name="op">运算符</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef MathClac_Func(OperandDef[] ops, OperatorType op)
        {
            if (ops.Length < 1)
                throw new ArgumentException($"{op}: 操作数小于1个");


            if (ops[0].DataType == DataTypeEnum.BOOLEAN || ops[0].DataType == DataTypeEnum.STRING)
                throw new ArgumentException($"{op}: 操作类型只能是整数或者浮点数");

            if (ops[0].DataType == DataTypeEnum.INTEGER)
            {
                ops[0].DataType = DataTypeEnum.DOUBLE;
                ops[0].dVal = ops[0].iVal;
            }

            if (op == OperatorType.LG && ops[0].dVal <= 0)
                throw new ArgumentException("lg: 数据小于等于0");

            if (op == OperatorType.SQRT && ops[0].dVal < 0)
                throw new ArgumentException("sqrt: 数据小于0");

            switch (op)
            {
                case OperatorType.TAN: return new OperandDef(Math.Tan(ops[0].dVal));
                case OperatorType.ATAN: return new OperandDef(Math.Atan(ops[0].dVal));
                case OperatorType.SIN: return new OperandDef(Math.Sin(ops[0].dVal));
                case OperatorType.ASIN: return new OperandDef(Math.Asin(ops[0].dVal));
                case OperatorType.COS: return new OperandDef(Math.Cos(ops[0].dVal));
                case OperatorType.ACOS: return new OperandDef(Math.Acos(ops[0].dVal));
                case OperatorType.ABS: return new OperandDef(Math.Abs(ops[0].dVal));
                case OperatorType.SQRT: return new OperandDef(Math.Sqrt(ops[0].dVal));
                case OperatorType.RAND: return new OperandDef(ops[0].dVal * ClacuationFormula.rand.NextDouble());
                case OperatorType.INT: return new OperandDef((int)ops[0].dVal);
                case OperatorType.LG: return new OperandDef(Math.Log10(ops[0].dVal));
            }

            throw new NotImplementedException($"{op}: 一元数学运算未实现");
        }

        /// <summary>
        /// 比较运算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <param name="op">运算符</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef CompareClac_Func(OperandDef[] ops, OperatorType op)
        {
            if (ops.Length < 2)
                throw new ArgumentException($"{op}: 操作数小于2个");

            if (ops[0].DataType == DataTypeEnum.INTEGER && ops[1].DataType == DataTypeEnum.DOUBLE)
            {
                ops[0].DataType = DataTypeEnum.DOUBLE;
                ops[0].dVal = ops[0].iVal;
            }

            if (ops[1].DataType == DataTypeEnum.INTEGER && ops[0].DataType == DataTypeEnum.DOUBLE)
            {
                ops[1].DataType = DataTypeEnum.DOUBLE;
                ops[1].dVal = ops[0].iVal;
            }

            if (ops[0].DataType != ops[1].DataType)
            {
                throw new ArgumentException($"{op}, {ops[0].DataType} 和 {ops[1].DataType}类型不同，不能比较");
            }


            if (ops[0].DataType == DataTypeEnum.BOOLEAN)
            {
                switch (op)
                {
                    case OperatorType.ET: return new OperandDef(ops[1].bVal == ops[0].bVal);
                    case OperatorType.UT: return new OperandDef(ops[1].bVal != ops[0].bVal);
                    case OperatorType.LT: throw new ArgumentException($"{op}: 布尔型不能比较大小");
                    case OperatorType.GT: throw new ArgumentException($"{op}: 布尔型不能比较大小");
                    case OperatorType.LE: throw new ArgumentException($"{op}: 布尔型不能比较大小");
                    case OperatorType.GE: throw new ArgumentException($"{op}: 布尔型不能比较大小");
                }
            }

            if (ops[0].DataType == DataTypeEnum.INTEGER)
            {
                switch (op)
                {
                    case OperatorType.ET: return new OperandDef(ops[1].iVal == ops[0].iVal);
                    case OperatorType.UT: return new OperandDef(ops[1].iVal != ops[0].iVal);
                    case OperatorType.LT: return new OperandDef(ops[1].iVal < ops[0].iVal);
                    case OperatorType.GT: return new OperandDef(ops[1].iVal > ops[0].iVal);
                    case OperatorType.LE: return new OperandDef(ops[1].iVal <= ops[0].iVal);
                    case OperatorType.GE: return new OperandDef(ops[1].iVal >= ops[0].iVal);
                }
            }

            if (ops[0].DataType == DataTypeEnum.DOUBLE)
            {
                switch (op)
                {
                    case OperatorType.ET: return new OperandDef(ops[1].dVal == ops[0].dVal);
                    case OperatorType.UT: return new OperandDef(ops[1].dVal != ops[0].dVal);
                    case OperatorType.LT: return new OperandDef(ops[1].dVal < ops[0].dVal);
                    case OperatorType.GT: return new OperandDef(ops[1].dVal > ops[0].dVal);
                    case OperatorType.LE: return new OperandDef(ops[1].dVal <= ops[0].dVal);
                    case OperatorType.GE: return new OperandDef(ops[1].dVal >= ops[0].dVal);
                }
            }

            if (ops[0].DataType == DataTypeEnum.STRING)
            {
                switch (op)
                {
                    case OperatorType.ET: return new OperandDef(ops[1].sVal == ops[0].sVal);
                    case OperatorType.UT: return new OperandDef(ops[1].sVal != ops[0].sVal);
                    case OperatorType.LT: throw new ArgumentException($"{op}: 字符串不能比较大小");
                    case OperatorType.GT: throw new ArgumentException($"{op}: 字符串不能比较大小");
                    case OperatorType.LE: throw new ArgumentException($"{op}: 字符串不能比较大小");
                    case OperatorType.GE: throw new ArgumentException($"{op}: 字符串不能比较大小");
                }
            }

            throw new NotImplementedException();
        }

        /// <summary>
        /// 逻辑运算
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <param name="op">运算符</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef LogicClac_Func(OperandDef[] ops, OperatorType op)
        {
            if (ops.Length < 2)
                throw new ArgumentException($"{op}: 操作数小于2个");

            if (ops[0].DataType != DataTypeEnum.BOOLEAN || ops[1].DataType != DataTypeEnum.BOOLEAN)
            {
                throw new ArgumentException($"{op}: 逻辑运算两边必须都是布尔型");
            }

            switch (op)
            {
                case OperatorType.AND: return new OperandDef(ops[1].bVal && ops[0].bVal);
                case OperatorType.OR : return new OperandDef(ops[1].bVal || ops[0].bVal);
            }

            throw new NotImplementedException();
        }

        /// <summary>
        /// 特殊功能 如ARMP/ANGLE等
        /// </summary>
        /// <param name="ops">运算数据</param>
        /// <param name="id">私有ID号</param>
        /// <param name="op">运算符</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public static OperandDef SpecClac_Func(OperandDef[] ops, Guid id, OperatorType op)
        {
            if (ops[0].DataType == DataTypeEnum.BOOLEAN || ops[0].DataType == DataTypeEnum.STRING)
                throw new ArgumentException($"{op}: 操作类型只能是整数或者浮点数");
            
            if ( op == OperatorType.SPEED)
            {
                var result = FormulaTools.Instance.Step(id, ops[0]);
                return new OperandDef(result);
            }
            
            if (op == OperatorType.RAMP || op == OperatorType.ANGLE || op == OperatorType.FILTER)
            {
                if (ops[1].DataType == DataTypeEnum.BOOLEAN || ops[1].DataType == DataTypeEnum.STRING)
                    throw new ArgumentException($"{op}: 操作类型只能是整数或者浮点数");

                var result = FormulaTools.Instance.Step(id, ops[1], ops[0]);
                return new OperandDef(result);
            }

            throw new NotImplementedException($"{op}: 特殊功能未实现");
        }

        /// <summary>
        /// 字符串计算
        /// </summary>
        /// <param name="ops"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static OperandDef Substr_Func(OperandDef[] ops)
        {
            if (ops.Length < 3)
            {
                throw new ArgumentException($"substr: 需要提供三个操作数，实际只有{ops.Length}");
            }

            if (ops[2].DataType != DataTypeEnum.STRING)
            {
                throw new ArgumentException($"substr: 第一个参数必须是字符串");
            }
 
            if (ops[1].DataType != DataTypeEnum.INTEGER)
            {
                throw new ArgumentException($"substr: 第二个参数必须是整数");
            }
            
            if (ops[0].DataType != DataTypeEnum.INTEGER)
            {
                throw new ArgumentException($"substr: 第三个参数必须是整数");
            }

            var str = (ops[2].sVal ??= "").Substring(ops[1].iVal, ops[0].iVal);
            return new OperandDef(str);
        }
    }
}
